#include "bsp.h"
#include "pincfg.h"
#include "bsp_atk_nand.h"

//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用，未经作者许可，不得用于其它任何用途
//ALIENTEK STM32开发板
//NAND FLASH 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/15
//版本：V1.5
//版权所有，盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//升级说明
//V1.1 20160520
//1,新增硬件ECC支持(仅在以NAND_ECC_SECTOR_SIZE大小为单位进行读写时处理)
//2,新增NAND_Delay函数,用于等待tADL/tWHR
//3,新增NAND_WritePageConst函数,用于搜寻坏块.
//V1.2 20160525
//1,去掉NAND_SEC_SIZE宏定义，由NAND_ECC_SECTOR_SIZE替代
//2,去掉nand_dev结构体里面的secbuf指针，用不到
//V1.3 20160907
//1,定义NAND_TADL_DELAY宏,用于设置tADL的延迟时间,方便修改
//V1.4 20180321
//1,新增对H27U4G8F2E芯片的支持
//2,修改MEMSET/MEMHOLD/MEMHIZ等延时,以支持H27U4G08F2E.
//V1.5 20180531
//1.增加NAND_TWHR_DELAY/NAND_TRHW_DELAY/NAND_TPROG_DELAY/NAND_TBERS_DELAY四个延时,防止出错
//2.擦除时增加ms级别的延时，防止擦除时间不够立马进行读状态而造成出错
//////////////////////////////////////////////////////////////////////////////////

#define delay_us bsp_delay_us
#define delay_ms bsp_delay_ms

NAND_HandleTypeDef NAND_Handler; //NAND FLASH句柄
nand_attriute atk_nand;			 //nand重要参数结构体

//初始化NAND FLASH
u8 atk_nand_init(void)
{
	FMC_NAND_PCC_TimingTypeDef ComSpaceTiming, AttSpaceTiming;

	NAND_Handler.Instance = FMC_NAND_DEVICE;
	NAND_Handler.Init.NandBank = FMC_NAND_BANK2;					   //NAND挂在BANK3上
	NAND_Handler.Init.Waitfeature = FMC_NAND_PCC_WAIT_FEATURE_DISABLE; //关闭等待特性
	NAND_Handler.Init.MemoryDataWidth = FMC_NAND_PCC_MEM_BUS_WIDTH_8;  //8位数据宽度
	NAND_Handler.Init.EccComputation = FMC_NAND_ECC_DISABLE;		   //不使用ECC
	NAND_Handler.Init.ECCPageSize = FMC_NAND_ECC_PAGE_SIZE_2048BYTE;   //ECC页大小为2k
	NAND_Handler.Init.TCLRSetupTime = 0;							   //设置TCLR(tCLR=CLE到RE的延时)=(TCLR+TSET+2)*THCLK,THCLK=1/180M=5.5ns
	NAND_Handler.Init.TARSetupTime = 1;								   //设置TAR(tAR=ALE到RE的延时)=(TAR+TSET+2)*THCLK,THCLK=1/180M=5.5n。

	ComSpaceTiming.SetupTime = 2;	  //建立时间2
	ComSpaceTiming.WaitSetupTime = 3; //等待时间3
	ComSpaceTiming.HoldSetupTime = 2; //保持时间2
	ComSpaceTiming.HiZSetupTime = 1;  //高阻态时间1

	AttSpaceTiming.SetupTime = 2;	  //建立时间2
	AttSpaceTiming.WaitSetupTime = 3; //等待时间3
	AttSpaceTiming.HoldSetupTime = 2; //保持时间2
	AttSpaceTiming.HiZSetupTime = 1;  //高阻态时间1

	HAL_NAND_Init(&NAND_Handler, &ComSpaceTiming, &AttSpaceTiming);
	atk_nand_reset(); //复位NAND
	delay_ms(100);
	atk_nand.id = atk_nand_read_id();	//读取ID
	atk_nand_mode_set(4);				//设置为MODE4,高速模式
	if (atk_nand.id == MT29F16G08ABABA) //NAND为MT29F16G08ABABA
	{
		atk_nand.page_totalsize = 4320; //nand一个page的总大小（包括spare区）
		atk_nand.page_mainsize = 4096;	//nand一个page的有效数据区大小
		atk_nand.page_sparesize = 224;	//nand一个page的spare区大小
		atk_nand.block_pagenum = 128;	//nand一个block所包含的page数目
		atk_nand.plane_blocknum = 2048; //nand一个plane所包含的block数目
		atk_nand.block_totalnum = 4096; //nand的总block数目
	}
	else if (atk_nand.id == MT29F4G08ABADA) //NAND为MT29F4G08ABADA
	{
		atk_nand.page_totalsize = 2112; //nand一个page的总大小（包括spare区）
		atk_nand.page_mainsize = 2048;	//nand一个page的有效数据区大小
		atk_nand.page_sparesize = 64;	//nand一个page的spare区大小
		atk_nand.block_pagenum = 64;	//nand一个block所包含的page数目
		atk_nand.plane_blocknum = 2048; //nand一个plane所包含的block数目
		atk_nand.block_totalnum = 4096; //nand的总block数目
	}
	else if (atk_nand.id == MT29F1G08ABADA) //NAND为MT29F1G08ABADA
	{
		atk_nand.page_totalsize = 2112; //nand一个page的总大小（包括spare区）
		atk_nand.page_mainsize = 2048;	//nand一个page的有效数据区大小
		atk_nand.page_sparesize = 64;	//nand一个page的spare区大小
		atk_nand.block_pagenum = 64;	//nand一个block所包含的page数目
		atk_nand.plane_blocknum = 1024; //nand一个plane所包含的block数目
		atk_nand.block_totalnum = 1024; //nand的总block数目
	}
	else
	{
		dbg_puts("nand id not used! \n");

		return 1; //错误，返回
	}
	return 0;
}

//NAND FALSH底层驱动,引脚配置，时钟使能
//此函数会被HAL_NAND_Init()调用
void HAL_NAND_MspInit(NAND_HandleTypeDef *hnand)
{
	intx_alloc();

	GPIO_InitTypeDef GPIO_Initure;

	intx_disable();

	__HAL_RCC_FMC_CLK_ENABLE(); //使能FMC时钟

	//初始化PD6 R/B引脚
	GPIO_Initure.Pin = GPIO_PIN_6;
	GPIO_Initure.Mode = GPIO_MODE_INPUT;  //输入
	GPIO_Initure.Pull = GPIO_PULLUP;	  //上拉
	GPIO_Initure.Speed = GPIO_SPEED_HIGH; //高速
	HAL_GPIO_Init(GPIOD, &GPIO_Initure);

	//初始化PG9 NCE3引脚
	GPIO_Initure.Pin = GPIO_PIN_9;
	GPIO_Initure.Mode = GPIO_MODE_AF_PP;	//输入
	GPIO_Initure.Pull = GPIO_NOPULL;		//上拉
	GPIO_Initure.Speed = GPIO_SPEED_HIGH;	//高速
	GPIO_Initure.Alternate = GPIO_AF12_FMC; //复用为FMC
	// HAL_GPIO_Init(GPIOG, &GPIO_Initure);

	//初始化PD0,1,4,5,11,12,14,15
	GPIO_Initure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7 |
					   GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_14 | GPIO_PIN_15;
	GPIO_Initure.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(GPIOD, &GPIO_Initure);

	//初始化PE7,8,9,10
	GPIO_Initure.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10;
	HAL_GPIO_Init(GPIOE, &GPIO_Initure);

	intx_enable();

	nand_busy.dir_in_up();
}

//读取NAND FLASH的ID
//返回值:0,成功;
//    其他,失败
u8 atk_nand_mode_set(u8 mode)
{
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_FEATURE; //发送设置特性命令
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = 0X01;		  //地址为0X01,设置mode
	*(vu8 *)NAND_ADDRESS = mode;					  //P1参数,设置mode
	*(vu8 *)NAND_ADDRESS = 0;
	*(vu8 *)NAND_ADDRESS = 0;
	*(vu8 *)NAND_ADDRESS = 0;
	if (atk_nand_wait_ready() == NSTA_READY)
		return 0; //成功
	else
		return 1; //失败
}

/*****************************************************************************
* @brief	atk_nand_read_id. FLASH的ID.
* 不同的NAND略有不同，请根据自己所使用的NAND FALSH数据手册来编写函数
* @param	none
* @return	NAND ID
*****************************************************************************/
u32 atk_nand_read_id(void)
{
	u8 deviceid[5];
	u32 id;
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_READID; //发送读取ID命令
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = 0X00;
	//ID一共有5个字节
	deviceid[0] = *(vu8 *)NAND_ADDRESS;
	deviceid[1] = *(vu8 *)NAND_ADDRESS;
	deviceid[2] = *(vu8 *)NAND_ADDRESS;
	deviceid[3] = *(vu8 *)NAND_ADDRESS;
	deviceid[4] = *(vu8 *)NAND_ADDRESS;
	//镁光的NAND FLASH的ID一共5个字节，但是为了方便我们只取4个字节组成一个32位的ID值
	//根据NAND FLASH的数据手册，只要是镁光的NAND FLASH，那么一个字节ID的第一个字节都是0X2C
	//所以我们就可以抛弃这个0X2C，只取后面四字节的ID值。
	id = ((u32)deviceid[1]) << 24 | ((u32)deviceid[2]) << 16 | ((u32)deviceid[3]) << 8 | deviceid[4];
	return id;
}

/*****************************************************************************
* @brief	atk_nand_read_status. 读NAND状态
* @param	none
* @return	NAND状态值
* bit0:0,成功;1,错误(编程/擦除/READ).
* bit6:0,Busy;1,Ready.
*****************************************************************************/
u8 atk_nand_read_status(void)
{
	vu8 data = 0;
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_READSTA; //发送读状态命令
	atk_nand_delay(NAND_TWHR_DELAY);				  //等待tWHR,再读取状态寄存器
	data = *(vu8 *)NAND_ADDRESS;					  //读取状态值
	return data;
}

/*****************************************************************************
* @brief	atk_nand_wait_ready. 等待NAND准备好
* @param	none
* @return	NSTA_READY-成功	NSTA_TIMEOUT-失败
*****************************************************************************/
u8 atk_nand_wait_ready(void)
{
	u8 status = 0;
	vu32 time = 0;
	while (1) //等待ready
	{
		status = atk_nand_read_status(); //获取状态值
		if (status & NSTA_READY)
			break;
		time++;
		if (time >= 0X1FFFFFFF)
			return NSTA_TIMEOUT; //超时
	}
	return NSTA_READY; //准备好
}

/*****************************************************************************
* @brief	atk_nand_reset. 复位NAND
* @param	none
* @return	ERR	0-成功	其他-失败
*****************************************************************************/
u8 atk_nand_reset(void)
{
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_RESET; //复位NAND
	if (atk_nand_wait_ready() == NSTA_READY)
		return 0; //复位成功
	else
		return 1; //复位失败
}

/*****************************************************************************
* @brief	atk_nand_wait_rb. 等待RB信号为某个电平
* @param	rb : 0:等待RB==0, 1:等待RB==1
* @return	ERR	0-成功	其他-失败
*****************************************************************************/
u8 atk_nand_wait_rb(vu8 rb)
{
	vu32 time = 0;
	uu8 rb_st;

	while (time < 0X1FFFFFF)
	{
		time++;
		rb_st = NAND_RB;
		if (rb_st == rb)
			return 0;
	}
	return 1;
}

/*****************************************************************************
* @brief	atk_nand_delay. NAND延时
* @param	i : i--，一个i至少需要4ns
* @return	ERR	0-成功	其他-失败
*****************************************************************************/
void atk_nand_delay(vu32 i)
{
	while (i > 0)
		i--;
}

/*****************************************************************************
* @brief	atk_nand_read_page. 读取指定页数据(main区和spare区都可以使用此函数)。
*			写入数量为ECCL倍数时执行ECC，colnum应对齐在ECC边界。
* @param	PageNum : 页地址0~(block_pagenum*block_totalnum-1)
* @param	ColNum  : cell子地址，范围:0~(page_totalsize-1)
* @param	pBuffer : 数据缓存
* @param	NumByteToWrite : 数据长度
* @return	ERR	0-成功	其他-失败
*****************************************************************************/
u8 atk_nand_read_page(u32 PageNum, u16 ColNum, u8 *pBuffer, u16 NumByteToRead)
{
	u16 i = 0;
	u8 res = 0;
	u8 eccnum = 0;	 //需要计算的ECC个数，每NAND_ECC_SECTOR_SIZE字节计算一个ecc
	u8 eccstart = 0; //第一个ECC值所属的地址范围
	u8 errsta = 0;
	u8 *p;
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_AREA_A;
	//发送地址
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)ColNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(ColNum >> 8);
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)PageNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(PageNum >> 8);
	if (atk_nand.block_totalnum > 1024)
	{
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(PageNum >> 16);
	}
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_AREA_TRUE1;
	//下面两行代码是等待R/B引脚变为低电平，其实主要起延时作用的，等待NAND操作R/B引脚。因为我们是通过
	//将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO，代码中通过读取NWAIT引脚的电平来判断NAND是否准备
	//就绪的。这个也就是模拟的方法，所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
	//闲状态，结果我们就读取了R/B引脚,这个时候肯定会出错的，事实上确实是会出错!大家也可以将下面两行
	//代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。
	res = atk_nand_wait_rb(0); //等待RB=0
	if (res)
	{
		return NSTA_TIMEOUT; //超时退出
	}
	//下面2行代码是真正判断NAND是否准备好的
	res = atk_nand_wait_rb(1); //等待RB=1
	if (res)
		return NSTA_TIMEOUT; //超时退出

	// ecc状态
	atk_nand.ecc_cok_st = 0;
	atk_nand.ecc_err_st = 0;

	if (NumByteToRead & (NAND_ECC_SECTOR_SIZE - 1)) //不是NAND_ECC_SECTOR_SIZE的整数倍，不进行ECC校验
	{
		//读取NAND FLASH中的值
		for (i = 0; i < NumByteToRead; i++)
		{
			*(vu8 *)pBuffer++ = *(vu8 *)NAND_ADDRESS;
		}
	}
	else
	{
		eccnum = NumByteToRead / NAND_ECC_SECTOR_SIZE; //得到ecc计算次数
		eccstart = ColNum / NAND_ECC_SECTOR_SIZE;
		p = pBuffer;
		for (res = 0; res < eccnum; res++)
		{
			FMC_Bank2_3->PCR2 |= 1 << 6;			   //使能ECC校验
			for (i = 0; i < NAND_ECC_SECTOR_SIZE; i++) //读取NAND_ECC_SECTOR_SIZE个数据
			{
				*(vu8 *)pBuffer++ = *(vu8 *)NAND_ADDRESS;
			}
			while (!(FMC_Bank2_3->SR2 & (1 << 6)))
				;													 //等待FIFO空
			atk_nand.ecc_hdbuf[res + eccstart] = FMC_Bank2_3->ECCR2; //读取硬件计算后的ECC值
			FMC_Bank2_3->PCR2 &= ~(1 << 6);							 //禁止ECC校验
		}
		i = atk_nand.page_mainsize + 0X10 + eccstart * 4; //从spare区的0X10位置开始读取之前存储的ecc值
		atk_nand_delay(NAND_TRHW_DELAY);				  //等待tRHW
		*(vu8 *)(NAND_ADDRESS | NAND_CMD) = 0X05;		  //随机读指令
		//发送地址
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)i;
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(i >> 8);
		*(vu8 *)(NAND_ADDRESS | NAND_CMD) = 0XE0; //开始读数据
		atk_nand_delay(NAND_TWHR_DELAY);		  //等待tWHR
		pBuffer = (u8 *)&atk_nand.ecc_rdbuf[eccstart];
		for (i = 0; i < 4 * eccnum; i++) //读取保存的ECC值
		{
			*(vu8 *)pBuffer++ = *(vu8 *)NAND_ADDRESS;
		}
		for (i = 0; i < eccnum; i++) //检验ECC
		{
			if (atk_nand.ecc_rdbuf[i + eccstart] != atk_nand.ecc_hdbuf[i + eccstart]) //不相等,需要校正
			{
				dbg_print("err hd,rd:0x%x,0x%x\r\n", atk_nand.ecc_hdbuf[i + eccstart], atk_nand.ecc_rdbuf[i + eccstart]);
				dbg_print("eccnum,eccstart:%d,%d\r\n", eccnum, eccstart);
				dbg_print("PageNum,ColNum:%d,%d\r\n", PageNum, ColNum);
				res = atk_nand_ecc_correct(p + NAND_ECC_SECTOR_SIZE * i, atk_nand.ecc_rdbuf[i + eccstart], atk_nand.ecc_hdbuf[i + eccstart]); //ECC校验
				if (res)
				{
					errsta = NSTA_ECC2BITERR; //标记2BIT及以上ECC错误
					atk_nand.ecc_err_st++;
				}
				else
				{
					errsta = NSTA_ECC1BITERR; //标记1BIT ECC错误
					atk_nand.ecc_cok_st++;
				}
			}
		}
	}

	if (atk_nand_wait_ready() != NSTA_READY)
		errsta = NSTA_ERROR; //失败

	return errsta; //成功
}

/*****************************************************************************
* @brief	atk_nand_read_cpage. 读取指定页数据
* @param	PageNum : 页地址0~(block_pagenum*block_totalnum-1)
* @param	pBuffer : 数据缓存
* @return	ERR	0-成功	其他-失败
*****************************************************************************/
u8 atk_nand_read_cpage(u32 PageNum, u8 *pBuffer)
{
	u16 i = 0;
	u8 res = 0;
	u8 eccnum = 0;	 //需要计算的ECC个数，每NAND_ECC_SECTOR_SIZE字节计算一个ecc
	u8 eccstart = 0; //第一个ECC值所属的地址范围
	u8 errsta = 0;
	u8 *p;

	atk_nand.ecc_err_st = 1;

	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_AREA_A;
	//发送地址
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)0;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)0;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)PageNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(PageNum >> 8);
	if (atk_nand.block_totalnum > 1024)
	{
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(PageNum >> 16);
	}
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_AREA_TRUE1;
	//下面两行代码是等待R/B引脚变为低电平，其实主要起延时作用的，等待NAND操作R/B引脚。因为我们是通过
	//将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO，代码中通过读取NWAIT引脚的电平来判断NAND是否准备
	//就绪的。这个也就是模拟的方法，所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
	//闲状态，结果我们就读取了R/B引脚,这个时候肯定会出错的，事实上确实是会出错!大家也可以将下面两行
	//代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。
	res = atk_nand_wait_rb(0); //等待RB=0
	if (res)
	{
		return NSTA_TIMEOUT; //超时退出
	}
	//下面2行代码是真正判断NAND是否准备好的
	res = atk_nand_wait_rb(1); //等待RB=1
	if (res)
		return NSTA_TIMEOUT; //超时退出

	// ecc状态
	atk_nand.ecc_cok_st = 0;
	atk_nand.ecc_err_st = 0;

	eccnum = atk_nand.page_mainsize / NAND_ECC_SECTOR_SIZE; //得到ecc计算次数
	eccstart = 0;

	p = pBuffer;
	// 读取主存区
	for (res = 0; res < eccnum; res++)
	{
		FMC_Bank2_3->PCR2 |= 1 << 6;			   //使能ECC校验
		for (i = 0; i < NAND_ECC_SECTOR_SIZE; i++) //读取NAND_ECC_SECTOR_SIZE个数据
		{
			*(vu8 *)pBuffer++ = *(vu8 *)NAND_ADDRESS;
		}
		while (!(FMC_Bank2_3->SR2 & (1 << 6)))
			;													 //等待FIFO空
		atk_nand.ecc_hdbuf[res + eccstart] = FMC_Bank2_3->ECCR2; //读取硬件计算后的ECC值
		FMC_Bank2_3->PCR2 &= ~(1 << 6);							 //禁止ECC校验
	}

	// 读取spare区
	for (i = 0; i < 16; i++)
	{
		*(vu8 *)pBuffer++ = *(vu8 *)NAND_ADDRESS;
	}

	// 读取ECC区
	pBuffer = (u8 *)&atk_nand.ecc_rdbuf[eccstart];
	for (i = 0; i < 4 * eccnum; i++) //读取保存的ECC值
	{
		*(vu8 *)pBuffer++ = *(vu8 *)NAND_ADDRESS;
	}

	//检验ECC
	for (i = 0; i < eccnum; i++)
	{
		if (atk_nand.ecc_rdbuf[i + eccstart] != atk_nand.ecc_hdbuf[i + eccstart]) //不相等,需要校正
		{
			dbg_print("err hd,rd:0x%x,0x%x\r\n", atk_nand.ecc_hdbuf[i + eccstart], atk_nand.ecc_rdbuf[i + eccstart]);
			dbg_print("eccnum,eccstart:%d,%d\r\n", eccnum, eccstart);
			dbg_print("PageNum,ColNum:%d,%d\r\n", PageNum, 0);
			res = atk_nand_ecc_correct(p + NAND_ECC_SECTOR_SIZE * i, atk_nand.ecc_rdbuf[i + eccstart], atk_nand.ecc_hdbuf[i + eccstart]); //ECC校验
			if (res)
			{
				errsta = NSTA_ECC2BITERR; //标记2BIT及以上ECC错误
				atk_nand.ecc_err_st++;
			}
			else
			{
				errsta = NSTA_ECC1BITERR; //标记1BIT ECC错误
				atk_nand.ecc_cok_st++;
			}
		}
	}

	// 等待就绪
	if (atk_nand_wait_ready() != NSTA_READY)
		errsta = NSTA_ERROR; //失败

	return errsta; //成功
}

/*****************************************************************************
* @brief   atk_nand_read_page_comp. 读取指定页指定列的数据(main区和spare区都可以使用此函数)
* @param   PageNum : 页地址0~(block_pagenum*block_totalnum-1)
* @param   ColNum  : cell子地址，范围:0~(page_totalsize-1)
* @param   CmpVal : 比较值
* @param   NumByteToWrite : 数据长度
* @return  ERR	0-成功	其他-失败
*****************************************************************************/
u8 atk_nand_read_page_comp(u32 PageNum, u16 ColNum, u32 CmpVal, u16 NumByteToRead, u16 *NumByteEqual)
{
	u16 i = 0;
	u8 res = 0;
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_AREA_A;
	//发送地址
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)ColNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(ColNum >> 8);
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)PageNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(PageNum >> 8);
	if (atk_nand.block_totalnum > 1024)
	{
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(PageNum >> 16);
	}
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_AREA_TRUE1;
	//下面两行代码是等待R/B引脚变为低电平，其实主要起延时作用的，等待NAND操作R/B引脚。因为我们是通过
	//将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO，代码中通过读取NWAIT引脚的电平来判断NAND是否准备
	//就绪的。这个也就是模拟的方法，所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
	//闲状态，结果我们就读取了R/B引脚,这个时候肯定会出错的，事实上确实是会出错!大家也可以将下面两行
	//代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。
	res = atk_nand_wait_rb(0); //等待RB=0
	if (res)
		return NSTA_TIMEOUT;   //超时退出
							   //下面2行代码是真正判断NAND是否准备好的
	res = atk_nand_wait_rb(1); //等待RB=1
	if (res)
		return NSTA_TIMEOUT;			//超时退出
	for (i = 0; i < NumByteToRead; i++) //读取数据,每次读4字节
	{
		if (*(vu32 *)NAND_ADDRESS != CmpVal)
			break; //如果有任何一个值,与CmpVal不相等,则退出.
	}
	*NumByteEqual = i; //与CmpVal值相同的个数
	if (atk_nand_wait_ready() != NSTA_READY)
		return NSTA_ERROR; //失败
	return 0;			   //成功
}

/*****************************************************************************
* @brief   atk_nand_write_page. 在一页中写入数据(main区和spare区都可以使用此函数)
* 写入数量为ECCL倍数时执行ECC，colnum应对齐在ECC边界
* @param   PageNum : 页地址0~(block_pagenum*block_totalnum-1)
* @param   ColNum  : cell子地址，范围:0~(page_totalsize-1)
* @param   pBuffer : 数据缓存
* @param   NumByteToWrite : 数据长度
* @return  ERR	0-成功	其他-失败
*****************************************************************************/
u8 atk_nand_write_page(u32 PageNum, u16 ColNum, u8 *pBuffer, u16 NumByteToWrite)
{
	vu16 i = 0;
	u8 res = 0;
	u8 eccnum = 0;	 //需要计算的ECC个数，每NAND_ECC_SECTOR_SIZE字节计算一个ecc
	u8 eccstart = 0; //第一个ECC值所属的地址范围

	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_WRITE0;
	//发送地址
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)ColNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(ColNum >> 8);
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)PageNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(PageNum >> 8);
	if (atk_nand.block_totalnum > 1024)
	{
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(PageNum >> 16);
	}
	atk_nand_delay(NAND_TADL_DELAY);				 //等待tADL
	if (NumByteToWrite & (NAND_ECC_SECTOR_SIZE - 1)) //不是NAND_ECC_SECTOR_SIZE的整数倍，不进行ECC校验
	{
		for (i = 0; i < NumByteToWrite; i++) //写入数据
		{
			*(vu8 *)NAND_ADDRESS = *(vu8 *)pBuffer++;
		}
	}
	else
	{
		eccnum = NumByteToWrite / NAND_ECC_SECTOR_SIZE; //得到ecc计算次数
		eccstart = ColNum / NAND_ECC_SECTOR_SIZE;
		for (res = 0; res < eccnum; res++)
		{
			FMC_Bank2_3->PCR2 |= 1 << 6;			   //使能ECC校验
			for (i = 0; i < NAND_ECC_SECTOR_SIZE; i++) //写入NAND_ECC_SECTOR_SIZE个数据
			{
				*(vu8 *)NAND_ADDRESS = *(vu8 *)pBuffer++;
			}
			while (!(FMC_Bank2_3->SR2 & (1 << 6)))
				;													 //等待FIFO空
			atk_nand.ecc_hdbuf[res + eccstart] = FMC_Bank2_3->ECCR2; //读取硬件计算后的ECC值
			FMC_Bank2_3->PCR2 &= ~(1 << 6);							 //禁止ECC校验
		}
		i = atk_nand.page_mainsize + 0X10 + eccstart * 4; //计算写入ECC的spare区地址
		atk_nand_delay(NAND_TADL_DELAY);				  //等待tADL
		*(vu8 *)(NAND_ADDRESS | NAND_CMD) = 0X85;		  //随机写指令
		//发送地址
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)i;
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(i >> 8);
		atk_nand_delay(NAND_TADL_DELAY); //等待tADL
		pBuffer = (u8 *)&atk_nand.ecc_hdbuf[eccstart];
		for (i = 0; i < eccnum; i++) //写入ECC
		{
			for (res = 0; res < 4; res++)
			{
				*(vu8 *)NAND_ADDRESS = *(vu8 *)pBuffer++;
			}
		}
	}
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_WRITE_TURE1;
	delay_us(NAND_TPROG_DELAY); //等待tPROG
	if (atk_nand_wait_ready() != NSTA_READY)
		return NSTA_ERROR; //失败
	return 0;			   //成功
}

/*****************************************************************************
* @brief   atk_nand_write_cpage. 在一页中写入数据
* @param   PageNum : 页地址0~(block_pagenum*block_totalnum-1)
* @param   pBuffer : 数据缓存
* @return  ERR	0-成功	其他-失败
*****************************************************************************/
u8 atk_nand_write_cpage(u32 PageNum, u8 *pBuffer)
{
	vu16 i = 0;
	u8 res = 0;
	u8 eccnum = 0;	 //需要计算的ECC个数，每NAND_ECC_SECTOR_SIZE字节计算一个ecc
	u8 eccstart = 0; //第一个ECC值所属的地址范围

	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_WRITE0;
	//发送地址
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)0;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)0;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)PageNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(PageNum >> 8);
	if (atk_nand.block_totalnum > 1024)
	{
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(PageNum >> 16);
	}
	atk_nand_delay(NAND_TADL_DELAY); //等待tADL

	//得到ecc计算次数
	eccnum = atk_nand.page_mainsize / NAND_ECC_SECTOR_SIZE;
	eccstart = 0;

	// 写主存数据
	for (res = 0; res < eccnum; res++)
	{
		FMC_Bank2_3->PCR2 |= 1 << 6;			   //使能ECC校验
		for (i = 0; i < NAND_ECC_SECTOR_SIZE; i++) //写入NAND_ECC_SECTOR_SIZE个数据
		{
			*(vu8 *)NAND_ADDRESS = *(vu8 *)pBuffer++;
		}
		while (!(FMC_Bank2_3->SR2 & (1 << 6)))
			;													 //等待FIFO空
		atk_nand.ecc_hdbuf[res + eccstart] = FMC_Bank2_3->ECCR2; //读取硬件计算后的ECC值
		FMC_Bank2_3->PCR2 &= ~(1 << 6);							 //禁止ECC校验
	}

	// 写spare数据
	for (i = 0; i < 16; i++)
	{
		*(vu8 *)NAND_ADDRESS = *(vu8 *)pBuffer++;
	}

	// 写ECC数据
	pBuffer = (u8 *)&atk_nand.ecc_hdbuf[eccstart];
	for (i = 0; i < eccnum; i++) //写入ECC
	{
		for (res = 0; res < 4; res++)
		{
			*(vu8 *)NAND_ADDRESS = *(vu8 *)pBuffer++;
		}
	}

	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_WRITE_TURE1;
	delay_us(NAND_TPROG_DELAY); //等待tPROG
	if (atk_nand_wait_ready() != NSTA_READY)
		return NSTA_ERROR; //失败
	return 0;			   //成功
}

/*****************************************************************************
* @brief   atk_nand_write_page_const. 在NAND一页中的指定地址开始,写入指定长度的恒定数字
* @param   PageNum : 页地址0~(block_pagenum*block_totalnum-1)
* @param   ColNum  : cell子地址，范围:0~(page_totalsize-1)
* @param   cval : 数据值
* @param   NumByteToWrite : 数据长度
* @return  ERR	0-成功	其他-失败
*****************************************************************************/
u8 atk_nand_write_page_const(u32 PageNum, u16 ColNum, u32 cval, u16 NumByteToWrite)
{
	u16 i = 0;
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_WRITE0;
	//发送地址
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)ColNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(ColNum >> 8);
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)PageNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(PageNum >> 8);
	if (atk_nand.block_totalnum > 1024)
	{
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(PageNum >> 16);
	}
	atk_nand_delay(NAND_TADL_DELAY);	 //等待tADL
	for (i = 0; i < NumByteToWrite; i++) //写入数据,每次写4字节
	{
		*(vu32 *)NAND_ADDRESS = cval;
	}
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_WRITE_TURE1;
	delay_us(NAND_TPROG_DELAY); //等待tPROG
	if (atk_nand_wait_ready() != NSTA_READY)
		return NSTA_ERROR; //失败
	return 0;			   //成功
}

/*****************************************************************************
* @brief   atk_nand_copy_page_without_write. 将一页数据拷贝到另一页,不写入新数据
* @param   Source_PageNum : 原页地址0~(block_pagenum*block_totalnum-1)
* @param   Dest_PageNum : 目标页地址0~(block_pagenum*block_totalnum-1)
* @return  ERR	0-成功	其他-失败
*****************************************************************************/
u8 atk_nand_copy_page_without_write(u32 Source_PageNum, u32 Dest_PageNum)
{
	u8 res = 0;
	u16 source_block = 0, dest_block = 0;
	//判断源页和目的页是否在同一个plane中
	source_block = Source_PageNum / atk_nand.block_pagenum;
	dest_block = Dest_PageNum / atk_nand.block_pagenum;
	if ((source_block % 2) != (dest_block % 2))
		return NSTA_ERROR;									//不在同一个plane内
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD0; //发送命令0X00
	//发送源页地址
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)0;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)0;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)Source_PageNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(Source_PageNum >> 8);
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(Source_PageNum >> 16);
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD1;
	//发送命令0X35
	//下面两行代码是等待R/B引脚变为低电平，其实主要起延时作用的，等待NAND操作R/B引脚。因为我们是通过
	//将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO，代码中通过读取NWAIT引脚的电平来判断NAND是否准备
	//就绪的。这个也就是模拟的方法，所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
	//闲状态，结果我们就读取了R/B引脚,这个时候肯定会出错的，事实上确实是会出错!大家也可以将下面两行
	//代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。
	res = atk_nand_wait_rb(0); //等待RB=0
	if (res)
		return NSTA_TIMEOUT;   //超时退出
							   //下面2行代码是真正判断NAND是否准备好的
	res = atk_nand_wait_rb(1); //等待RB=1
	if (res)
		return NSTA_TIMEOUT;								//超时退出
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD2; //发送命令0X85
	//发送目的页地址
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)0;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)0;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)Dest_PageNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(Dest_PageNum >> 8);
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(Dest_PageNum >> 16);
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD3; //发送命令0X10
	delay_us(NAND_TPROG_DELAY);								//等待tPROG
	if (atk_nand_wait_ready() != NSTA_READY)
		return NSTA_ERROR; //NAND未准备好
	return 0;			   //成功
}

/*****************************************************************************
* @brief   atk_nand_copy_page_with_write. 
* @param   Source_PageNum : 原页地址0~(block_pagenum*block_totalnum-1)
* @param   Dest_PageNum : 目标页地址0~(block_pagenum*block_totalnum-1)
* @param   ColNum  : 子地址，0~(page_sparesize-1)
* @param   pBuffer : 数据首地址
* @param   NumByteToWrite : 要读取的字节数(不大于page_sparesize)
* @return  ERR	0-成功	其他-失败
*****************************************************************************/
u8 atk_nand_copy_page_with_write(u32 Source_PageNum, u32 Dest_PageNum, u16 ColNum, u8 *pBuffer, u16 NumByteToWrite)
{
	u8 res = 0;
	vu16 i = 0;
	u16 source_block = 0, dest_block = 0;
	u8 eccnum = 0;	 //需要计算的ECC个数，每NAND_ECC_SECTOR_SIZE字节计算一个ecc
	u8 eccstart = 0; //第一个ECC值所属的地址范围
	//判断源页和目的页是否在同一个plane中
	source_block = Source_PageNum / atk_nand.block_pagenum;
	dest_block = Dest_PageNum / atk_nand.block_pagenum;
	if ((source_block % 2) != (dest_block % 2))
		return NSTA_ERROR;									//不在同一个plane内
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD0; //发送命令0X00
	//发送源页地址
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)0;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)0;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)Source_PageNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(Source_PageNum >> 8);
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(Source_PageNum >> 16);
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD1; //发送命令0X35

	//下面两行代码是等待R/B引脚变为低电平，其实主要起延时作用的，等待NAND操作R/B引脚。因为我们是通过
	//将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO，代码中通过读取NWAIT引脚的电平来判断NAND是否准备
	//就绪的。这个也就是模拟的方法，所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
	//闲状态，结果我们就读取了R/B引脚,这个时候肯定会出错的，事实上确实是会出错!大家也可以将下面两行
	//代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。
	res = atk_nand_wait_rb(0); //等待RB=0
	if (res)
		return NSTA_TIMEOUT;   //超时退出
							   //下面2行代码是真正判断NAND是否准备好的
	res = atk_nand_wait_rb(1); //等待RB=1
	if (res)
		return NSTA_TIMEOUT;								//超时退出
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD2; //发送命令0X85
	//发送目的页地址
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)ColNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(ColNum >> 8);
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)Dest_PageNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(Dest_PageNum >> 8);
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(Dest_PageNum >> 16);
	//发送页内列地址
	atk_nand_delay(NAND_TADL_DELAY);		   //等待tADL
	if (NumByteToWrite % NAND_ECC_SECTOR_SIZE) //不是NAND_ECC_SECTOR_SIZE的整数倍，不进行ECC校验
	{
		for (i = 0; i < NumByteToWrite; i++) //写入数据
		{
			*(vu8 *)NAND_ADDRESS = *(vu8 *)pBuffer++;
		}
	}
	else
	{
		eccnum = NumByteToWrite / NAND_ECC_SECTOR_SIZE; //得到ecc计算次数
		eccstart = ColNum / NAND_ECC_SECTOR_SIZE;
		for (res = 0; res < eccnum; res++)
		{
			FMC_Bank2_3->PCR2 |= 1 << 6;			   //使能ECC校验
			for (i = 0; i < NAND_ECC_SECTOR_SIZE; i++) //写入NAND_ECC_SECTOR_SIZE个数据
			{
				*(vu8 *)NAND_ADDRESS = *(vu8 *)pBuffer++;
			}
			while (!(FMC_Bank2_3->SR2 & (1 << 6)))
				;													 //等待FIFO空
			atk_nand.ecc_hdbuf[res + eccstart] = FMC_Bank2_3->ECCR2; //读取硬件计算后的ECC值
			FMC_Bank2_3->PCR2 &= ~(1 << 6);							 //禁止ECC校验
		}
		i = atk_nand.page_mainsize + 0X10 + eccstart * 4; //计算写入ECC的spare区地址
		atk_nand_delay(NAND_TADL_DELAY);				  //等待tADL
		*(vu8 *)(NAND_ADDRESS | NAND_CMD) = 0X85;		  //随机写指令
		//发送地址
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)i;
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(i >> 8);
		atk_nand_delay(NAND_TADL_DELAY); //等待tADL
		pBuffer = (u8 *)&atk_nand.ecc_hdbuf[eccstart];
		for (i = 0; i < eccnum; i++) //写入ECC
		{
			for (res = 0; res < 4; res++)
			{
				*(vu8 *)NAND_ADDRESS = *(vu8 *)pBuffer++;
			}
		}
	}
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_MOVEDATA_CMD3; //发送命令0X10
	delay_us(NAND_TPROG_DELAY);								//等待tPROG
	if (atk_nand_wait_ready() != NSTA_READY)
		return NSTA_ERROR; //失败
	return 0;			   //成功
}

/*****************************************************************************
* @brief   atk_nand_read_spare. 从spare区中读数据
* @param   PageNum : 页地址0~(block_pagenum*block_totalnum-1)
* @param   ColNum  : spare区子地址，0~(page_sparesize-1)
* @param   pBuffer : 数据首地址
* @param   NumByteToWrite : 要读取的字节数(不大于page_sparesize)
* @return  ERR	0-成功	其他-失败
*****************************************************************************/
u8 atk_nand_read_spare(u32 PageNum, u16 ColNum, u8 *pBuffer, u16 NumByteToRead)
{
	u8 temp = 0;
	u8 remainbyte = 0;
	remainbyte = atk_nand.page_sparesize - ColNum;
	//确保要写入的字节数不大于spare剩余的大小
	if (NumByteToRead > remainbyte)
		NumByteToRead = remainbyte;
	//读取数据
	temp = atk_nand_read_page(PageNum, ColNum + atk_nand.page_mainsize, pBuffer, NumByteToRead);
	return temp;
}

/*****************************************************************************
* @brief   atk_nand_write_spare. 向spare区中写数据
* @param   PageNum : 页地址0~(block_pagenum*block_totalnum-1)
* @param   ColNum  : spare区子地址，0~(page_sparesize-1)
* @param   pBuffer : 要写入的数据首地址
* @param   NumByteToWrite : 要写入的字节数(不大于page_sparesize)
* @return  ERR	0-成功	其他-失败
*****************************************************************************/
u8 atk_nand_write_spare(u32 PageNum, u16 ColNum, u8 *pBuffer, u16 NumByteToWrite)
{
	u8 temp = 0;
	u8 remainbyte = 0;
	remainbyte = atk_nand.page_sparesize - ColNum;

	//确保要读取的字节数不大于spare剩余的大小
	if (NumByteToWrite > remainbyte)
		NumByteToWrite = remainbyte;
	// 写入
	temp = atk_nand_write_page(PageNum, ColNum + atk_nand.page_mainsize, pBuffer, NumByteToWrite);
	return temp;
}

/*****************************************************************************
* @brief   atk_nand_erase_block.
* @param   BlockNum : 块地址0-(block_totalnum-1)
* @return  ERR
*****************************************************************************/
u8 atk_nand_erase_block(u32 BlockNum)
{
	if (atk_nand.id == MT29F16G08ABABA)
		BlockNum <<= 7; //将块地址转换为页地址
	else if (atk_nand.id == MT29F4G08ABADA)
		BlockNum <<= 6;
	else
		BlockNum *= atk_nand.block_pagenum;

	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_ERASE0;
	//发送块地址
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)BlockNum;
	*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(BlockNum >> 8);
	if (atk_nand.block_totalnum > 1024)
	{
		*(vu8 *)(NAND_ADDRESS | NAND_ADDR) = (u8)(BlockNum >> 16);
	}
	*(vu8 *)(NAND_ADDRESS | NAND_CMD) = NAND_ERASE1;

	delay_ms(NAND_TBERS_DELAY); //等待擦除成功
	if (atk_nand_wait_ready() != NSTA_READY)
		return NSTA_ERROR; //失败

	return 0; //成功
}

/*****************************************************************************
* @brief   atk_nand_erase_chip. 全片擦除NAND FLASH
* @param   none
* @return  none
*****************************************************************************/
void atk_nand_erase_chip(void)
{
	u8 status;
	u16 i = 0;
	for (i = 0; i < atk_nand.block_totalnum; i++) //循环擦除所有的块
	{
		status = atk_nand_erase_block(i);
		if (status)
			dbg_print("Erase %d block fail!!，错误码为%d\r\n", i, status); //擦除失败
	}
}

/*****************************************************************************
* @brief   atk_nand_get_OE. 获取ECC的奇数位/偶数位
* @param   oe :0,偶数位, 1,奇数位
* @param   eccval:输入的ecc值
* @return  计算后的ecc值(最多16位)
*****************************************************************************/
u16 atk_nand_get_OE(u8 oe, u32 eccval)
{
	u8 i;
	u16 ecctemp = 0;
	for (i = 0; i < 24; i++)
	{
		if ((i % 2) == oe)
		{
			if ((eccval >> i) & 0X01)
				ecctemp += 1 << (i >> 1);
		}
	}
	return ecctemp;
}

/*****************************************************************************
* @brief   atk_nand_ecc_correct. ECC校正函数
* @param   eccrd:读取出来,原来保存的ECC值
* @param   ecccl:读取数据时,硬件计算的ECC
* @return  0,错误已修正, 其他,ECC错误(有大于2个bit的错误,无法恢复)
*****************************************************************************/
u8 atk_nand_ecc_correct(u8 *data_buf, u32 eccrd, u32 ecccl)
{
	u16 eccrdo, eccrde, eccclo, ecccle;
	u16 eccchk = 0;
	u16 errorpos = 0;
	u32 bytepos = 0;
	eccrdo = atk_nand_get_OE(1, eccrd); //获取eccrd的奇数位
	eccrde = atk_nand_get_OE(0, eccrd); //获取eccrd的偶数位
	eccclo = atk_nand_get_OE(1, ecccl); //获取ecccl的奇数位
	ecccle = atk_nand_get_OE(0, ecccl); //获取ecccl的偶数位
	eccchk = eccrdo ^ eccrde ^ eccclo ^ ecccle;
	if (eccchk == 0XFFF) //全1,说明只有1bit ECC错误
	{
		errorpos = eccrdo ^ eccclo;
		dbg_print("errorpos:%d\r\n", errorpos);
		bytepos = errorpos / 8;
		data_buf[bytepos] ^= 1 << (errorpos % 8);
	}
	else //不是全1,说明至少有2bit ECC错误,无法修复
	{
		dbg_print("2bit ecc error or more\r\n");
		return 1;
	}
	return 0;
}
